Spring Cloud Alibaba


近年来随着互联网技术的发展,Alibaba 在微服务的贡献出的项目也非常得多,如 Dubbo 在 2021 年进行开源,之后迅速进行发展,直到 Spring Cloud 的出现来打破了传统的维度,之后 2017 年 Dubbo 项目又重新开始活跃,并拥抱 Spring Cloud 标准,也成为了目前国内为数不多可以替代 Netfilx 全家桶的一系列产品。

Spring Cloud Alibaba 系列的产品很受国内开发者喜爱,他也不仅仅是对本土开发者的支持和用户体验,而是这几年来 Alibaba 的产品经验,因此 Alibaba 的解决方案比 Netfile 更加更加人性化和好理解,但缺点在于文档支持没有 Netfile 的生态那么好。

对于 Spring Cloud Alibaba 中主要的组建就是 Dubbo、Nacos、Sentinel、Seata、RocketMQ

Dubbo

一个并不简单的 RPC 框架,用于实现多个系统之间的高性能、透明化调用、服务治理(注册、监控、路由、容错)

ZooKeeper

可以根据自身特性实现出注册中心,但他本身就是一个分布式协调中间件,因此实现注册中心也是合情合理。

Nacos

致力与解决为服务中的统一配置,服务注册与发现等问题,因此可以通过他来实现服务发现、服务配置、服务元数据和流量管理等

Sentinel

面向分布式服务架构的轻量级服务控制组建,可以实现对流量的降级和熔断以及一系列的限流规则。

Seata

开源的分布式事务解决方案,致力与在微服务中提供高性能和简单易用的分布式事务服务,提供了 AT、TCC、Saga、XA 事务模式

RocketMQ

是一个低延迟、高可靠、可伸缩易用于分布式消息中间件,经过 Alibaba 多年的 双 11 验证,因此具有了高吞吐、低延迟、海量消息堆积等特点,同时还提供了顺序消息,事物消息,定时消息,消息重试、消息追踪等特点。

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Spring Cloud Aliababa Sentinel

Sentinel 是 Alibaba Spring Cloud 微服务组件之一,他以 流量为切入点,主要实现流量控制、熔断降级、系统负载保护等多个维度来保证服务的稳定性,因此他还提供了机器发现、健康情况、监控、规则管理和推送等功能,并提供了一个可视化控制页面 Sentinel Dashboard,可前往 https://github.com/alibaba/Sentinel/releases/tag/1.8.2 进行下载,之后可以通过下述命令启动(Sentinel Dashboard 的帐号密码同样都为 sentinel):

1
sudo java -Dserver.port=9899 -Dcsp.sentinel.dashboard.server=localhost:9899 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar

限流模式

直接失败

通过 postman 进行测试

直接限流

当实例运行后,进入该实例中,在控制台中的流控规则中添加一个控流规则,并设置 QPS 为 1,并选择直接控流模式,而这个规则所达到的效果就是让超过阀值的请求直接失效,因此我们需要在项目中添加所需依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

在配置中我们依然只需要连接 Sentinel 控制台即可,并配置项目的名称,这将会在控制台中进行显示状态以及管理等。

1
2
3
4
5
6
7
8
9
spring:
application:
name: alibaba-sentinel
cloud:
sentinel:
transport:
dashboard: localhost:9899
server:
port: 8210
自定义埋点

所谓 “埋点” 我们可以理解为当用户访问特定的某个点,也就是监听这个点是否被用户所访问,在 Sentinel 中,我们可以通过 @SentinelResource 依赖中的埋点与 Sentinel 控制台中所增加的规则进行绑定,从而实现直接限流的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.demo.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 自定义埋点
*
* @author kunlun
* @date 2021/8/1
*/

@RestController
public class heyController {

@GetMapping("/hey")
@SentinelResource("hey")
public String hey() {
return "Server ok!";
}
}

当这完成之后,我们在 Sentinel 控制台中所增加的控流规则 QPS=1 当超过这个阀值的时候,该请求也会快速失败。

关联限流

关联限流简单理解就是,当我访问 test/one 的 QPS=1 的阀值过了以后,那么 test/two 这个接口就无法进行访问,因此我们在直接限流的基础上可以不需要设置埋点,直接添加两个接口即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 关联限流
*
* @author kunlun
* @date 2021/8/1
*/

@RestController
@RequestMapping("/test")
public class heyController {

@GetMapping("/one")
public String one() {
return "Server one ok!";
}

@GetMapping("/two")
public String two() {
return "Server two ok!";
}

}

链路限流


链路路由主要的作用就是,假设我们的接口都使用一个方法接口,那么在 Sentinel 中所填写的控流模式的入口资源,当入口资源超过 QPS 阀值时,那么将会被限流,但同样使用方法接口的另一个接口不会受此影响,为实现链路路由,我们需要添加如下依赖和增加配置:

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>

之后在配置文件中将是否统一网络上下文中选择 false:

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: alibaba-sentinel
cloud:
sentinel:
transport:
dashboard: localhost:9899
web-context-unify: false
server:
port: 8210
server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo.server;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;

/**
* 服务接口
*
* @author kunlun
* @date 2021/8/1
*/
@Service
public class sentinelServer {

@SentinelResource("message")
public void sentinelTest() {
System.out.println("sentinel is Test.");
}
}
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.example.demo.controller;

import com.example.demo.server.sentinelServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 链路限流
*
* @author kunlun
* @date 2021/8/1
*/

@RestController
@RequestMapping("/test")
public class heyController {

@Autowired
private sentinelServer sentinelServer;

@GetMapping("/one")
public String one() {
sentinelServer.sentinelTest();
System.out.println("Server two ok!");
return "Server one ok!";
}

@GetMapping("/two")
public String two() {
sentinelServer.sentinelTest();
System.out.println("Server two ok!");
return "Server two ok!";
}

}

Warm Up

Warm Up 限流的主要作用就是为了应对猛烈的流量请求,分批次的进行处理,之后慢慢加流量,而不是向 DDos 一样直接就把服务干崩溃了。做一个很形象的比喻,我们吃饭的时候都是嚼了好几下才开始咽下去的,而一些猛人吃饭跟喝水一样,最终导致食物卡到了喉咙了,而这种情况在限流中也同样存在,因此 Warm Up 就很好的解决了这个问题。

排队限流

排队等待的限流模式主要通过超时时间,也就是每个请求的处理时常,以此让服务匀速处理请求而不是直接拒绝请求的作用,在新的控流规则中,我们设置 QPS 为 4,而超时时间(每个请求的处理时间为 1000ms),从而我们的服务性能可以处理,但如果超时时间设置为 100ms 时,则会发现被拦截的请求很多,这是因为服务的处理性能达不到预设的超时时间而造成的。

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Spring Cloud Alibaba Nacos


Spring Cloud Alibaba 是一个遵循 Spring Cloud 变成规范的框架,也就是 Alibaba 所推出的微服务解决方案,他主要分为 Sentinel(流量控制)、Nacos(服务发现)、RocketMQ(分布式消息系统)、Dubbo(高性能 Java RPC 框架)、Seata(高性能微服务事务解决方案)、ACM(配置中中心)、OSS(对象存储服务)、SchedulerX(分布式任务调度)、SMS(短信服务)

Alibaba 对为 Spring Cloud 的贡献同样可以与 Netfilx 相同的产品都有类似的解决方案,但在 Spring Cloud 中 Netfilx 的贡献要比 Alibaba 早上许多,因此通常 Alibaba 的解决方案依然可以替换掉 Netfilx 的产品,但 Netfilx 同样也可以替换掉 Alibaba 的解决方案,与其相比之下 Alibaba 的解决方案更加的简洁,通常一个项目可以代替很多个 Netfilx 的子项目。

Nacos 是 Alibaba Spring Cloud 中用于构建微服务应用与服务治理的配置管理组建,与 Eureka、Conusl 同样分为服务中心、服务提供者、服务消费者等三个角色,除此之外还可以直接实现配置中心的服务。

在实现服务提供者和消费者之前,我们首先需要通过 Nacos 仓库来下载其发行版本的服务中心,需要注意的是环境环境依赖是:

  1. 64bit 支持 Linux\Unix\Mac\Windows(官方推荐 Linux\Mac\Unix)
  2. JDK 1.8+
  3. Maven 3.2.+

满足依赖环境后可以通过 https://github.com/alibaba/nacos/releases/tag/2.0.3 来获取你想要的版本进行下载。

之后解压进入 bin 目录,并运行 ./startup.sh -m standalone 命令以启动 Nacos,并访问 http://localhost:8848/nacos 即可登入 nacos 的控制台,帐号密码均为 nacos,因此服务中心的地址为 localhost:8848

服务治理

服务提供者


我们首先创建两个服务提供者,并连接服务中心,之后进行配置,在默认情况下我们需要引入 spring-boot-starter-web、spring-cloud-starter-alibaba-nacos-discovery 依赖以作为必要的服务支撑:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

同样的我们需要进行配置连接服务中心以及名称等,以便在微服务系统中发现该功能是那个服务所提供的,因此好进行改进和调整,配置后我们还需要在启动类中添加 @EnableDiscoveryClient 依赖

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: localhost-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848

server:
port: 8210

heyController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 提供者控制器
*
* @author kunlun
* @date 2021/8/1
*/
@RestController
public class heyController {

@Value("${spring.application.name}")
private String appName;

@GetMapping("/hey")
public String hey() {
String sty = "This is Provider: " + appName + "-1";
return sty;
}

}

从上所述我们的服务提供者所提供的基础服务已经构建完成,在演示中我们构建的是两个服务提供者,都提供同样的功能,端口为 8210~8211 ,为两个服务提供者,之后我们可以编写服务消费者。

服务消费者

至于服务消费者,我们同样的需要引入 spring-cloud-starter-alibaba-nacos-discovery 依赖以及负载均衡 feign 依赖,和一些基础服务提供依赖,来完成服务消费者的构建,在此之前我们需要在启动类中添加 @EnableDiscoveryClient 注解并完善配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置文件的主要作用同样是配置服务名称以及连接服务中心等:

1
2
3
4
5
6
7
8
9
spring:
application:
name: localhost-consumer
cloud:
nacos:
server-addr: 127.0.0.1:8848

server:
port: 8310

heyController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.example.demo.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.URI;

/**
* @author kunlun
* @date 2021/7/31
*/
@RestController
@EnableDiscoveryClient
public class heyController {

@Autowired
private LoadBalancerClient loadBalancerClient;

@Autowired
private RestTemplate restTemplate;

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

@Value("${spring.application.name}")
private String appName;

@GetMapping("/hey")
public String echoApplication() {
ServiceInstance serviceInstance = loadBalancerClient.choose("localhost-provider");
URI uri = serviceInstance.getUri();
String callService = new RestTemplate().getForObject(uri + "/hey", String.class);
return callService;
}

}

配置中心

相对与之前的 Spring Cloud Config ,Alibaba 所提供的 Spring Cloud Aliababa Config 主要依赖于服务中心的分发与配置,同时还支持配置动态刷新,以及达到和 Spring Cloud Config 相同效果的配置文件应用,只需要依赖 spring-cloud-starter-alibaba-nacos-config 即可,在此之前我们还需要在启动类中加入 @EnableDiscoveryClient 依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

在此之前,我们需要在服务中心中新建一个 .properties 类型的文件,当然他默认支持了 TEXT、JSON、XML、YAML、HTML、Properties 等配置格式,我们只需要填写配置信息和 Data ID 即可:

1
2
3
app.version=rsa
message=Spring Cloud Alibaba Config Server
server.port=9510

bootstrap.yml & application.yml

bootstrap.yml

1
2
3
4
5
spring:
cloud:
nacos:
config:
refreshable-dataids: server-config-example.properties

我们通过 refreshable-dataids 来绑定配置文件的名称,之后通过服务中心进行获取,因为是从服务中心直接获取,从而省去了很多配置。

application.yml

1
2
3
4
5
6
7
8
9
spring:
application:
name: localhost-consumer
cloud:
nacos:
server-addr: 127.0.0.1:8848

server:
port: 8410

Controller


控制器主要作用就是通过服务中心以及配置所绑定的文件,来获取到服务中心的配置文件信息,并通过指定接口来进行输出,当一切开始运行后访问 http://localhost:9510/hey 即可看到从服务中心和控制器所输出的信息。

ServerConfigController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 获取服务中心配置文件信息
*
* @author kunlun
* @date 2021/8/1
*/
@RestController
@RefreshScope
public class ServerConfigConortller {

@Value("${app.version}")
private String appVersion;

@Value("${message}")
private String message;

@RequestMapping("/hey")
public String hey() {
return "application: " + appVersion + " message: " + message;
}
}

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Spring Cloud Config


Spring Cloud Config 可以为分布式系统中外部化配置提供服务器端和客户端的支持,通过 Config Service 即配置中心,可以集中管理所有环境中应用程序配置,这其中主要用于集中管理信息的组件,用于提供配置变更、配置推送、历史版本管理以及灰度发布、配置变更审计等主要的功能,以此来降低分布式系统中所管理和配置信息的成本。

其实 Spring Cloud Config 说简单一点就是通过本地(含 Git/远程代码仓库)来获取配置信息,然后服务提供者A、B…… 从配置中心获取服务,在这个过程中有一个 Spring Cloud Bus 即消息总线,用于通知服务提供者需要获取配置。

目前主流的配置中心有 Spring Cloud Config、Apollo、Nacos、Disconf 等项目,其中 Spring Cloud Config 与 Disconf 是这些项目中最早开源的项目。

但这些项目中,Spring Cloud Config 他的功能全面,以及无缝贴合 Spring 体系,因此大受欢迎。

Config Server

localhost


目前主要且流行的方式就是通过 git 仓库来获取多个或多个配置文件,并通过对称/非对称加密来对密文转为明文,这都的易于 Spring Cloud Config 的特性,如果只满足简单的获取配置文件只需要添加 spring-cloud-config-server 依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

之后在其启动类中加入注解 @EnableConfigServer,并通过 application.yml 全局 Spring 配置中设置仓库地址和帐号密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
application:
name: cloud-server
cloud:
config:
server:
git:
uri: https://gitee.com/sif_one/spring-cloud-config
search-paths: config-repositories
username: xxx
password: xxx
encrypt:
enabled: true # 是否进行解密

server:
port: 8210

git

对于 git 仓库中的配置文件,需要注意的是 - 在访问时会被 / 所代替,如你的配置文件名为 config-dev.properties,那么所请求的 URI 就为 http://localhost:8210/config/dev,当然这是 json 格式的,如果想以常用的格式进行返回,可将 URI 改为其配置文件名 http://localhost:8210/config-dev.properties 即可。

1
2
3
4
app.version=dev
message=Spring Cloud Config Demo
password={cipher}32fd20f46a9c00238236ff530349eefb1dbaad99374c62276f266c326fa8e1ec
server.port=8017

Config Server Client


Spring cloud config client 的主要作用就是通过配置中心来获取配置并使用,主要通过 spring-cloud-starter-config 依赖来进行实现,并通过 bootstrap.yml 文件来达到配置文件切换的效果,最后在使用控制器进行输出:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

bootstrap.yml and application.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: config
profile: rsa
uri: http://localhost:8210
label: master

在 bootstrap.yml 文件中,你可以通过 profile 来切换配置文件的数据,也就是 name(config)profile(rsa),这也是我们配置文件 config-rsa 拼合而成。当然你也可以在 application.yml Spring 全局配置文件中来为其添加应用名称,毕竟他才是权重第一的配置文件。

1
2
3
spring:
application:
name: SpringCloudConfigClient

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 获取配置中心的数据
*
* @author kunlun
* @date 2021/7/26
*/
@RestController
public class heyController {

@Value("${app.version}")
private String version;

@Value("${message}")
private String message;

@RequestMapping("/hey")
public String hey() {
String string = "message:" + message + ":" + " version:" + version;
return string;
}
}

配置文件的加密

对称加密

Config Server 除了管理配置文件和版本管理之外,还支持对文件的对称和非对称加密,其中对称加密简单的来将就是采用单个密钥的方式进行加密,也就是说一个密钥可以加密和解密。

而非对称加密就与对称加密不同了,非对称加密算法分为公钥和私钥,如果用公钥对数据进行加密,那么只能通过私钥才可解密。目前有很多文章说需要使用新版本的 JDK 或者通过 JCE 来进行解决,当然这也是解决方法之一,在新版的 Spring 中,需要引入除 spring-cloud-config-server 之外的新依赖,即 spring-cloud-starter-bootstrap 依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

此时 spring-cloud-starter-bootstrap 会提供三个端口,分别用于加密、解密、检查等方式进行对称加密的操作:

Id Name Info Uri Method
1 /encrypt/status 检查加密密钥是否设置成功 http://localhost:8210/encrypt/status GET
2 /encrypt 加密内容 http://localhost:8210/encrypt POST
3 /decrypt 解密内容 http://localhost:8210/decrypt POST

在原有的 Spring Cloud Config 基础上,我们只需要新建一个 bootstrap.properties 文件并写入密钥,这主要的作用就是,你请求这个接口显示的是明文,而没有密钥的客户端发起请求返回的则是密文。

1
encrypt.key=hey

当然你如果有不良癖好喜欢密文的话,也可以通过 encrypt 来停止 Spring cloud config 的直接翻译,这时返回的则是密文信息:

1
2
3
4
5
6
spring:
cloud:
config:
server:
encrypt:
enabled: true # 是否进行解密

非对称加密


至于非对称加密则需要通过 keytools 来新建一个密钥,之后生成一个密钥移动到 resourcs 目录下,并在 bootstrap 全局配置文件 bbootstrap.applicatiyon 下填写相关的配置信息即可。

1
keytool -genkeypair -alias "spring-cloud-config" -keypass "keypass" -keyalg "RSA" -storepass "stropess" -keystore "spring-cloud-config.jks"

其中上述命令最为关键的部分则是 alias(别名)keypass(密钥口令)storepass(密钥库口令) 这三类主要配置,将会在配置文件中进行使用以验证身份,而 -keylag 则主要表示密钥的类型为 RSA 加密算法。

RSA是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1977年一起提出的,当时他们三人都在麻省理工学院工作,RSA 就是他们三人姓氏开头字母拼在一起组成的。

1
2
3
4
5
6
7
8
# 密钥位置
encrypt.key-store.location=classpath:/spring-cloud-config.jks
# 密钥别名
encrypt.key-store.alias=spring-cloud-config
# 密钥库密码
encrypt.key-store.secret=
# 密钥密码
encrypt.key-store.password=

与对称加密一样,可以通过之前的三个端点来进行加密、解密、状态检查等服务:

Id Name Info Uri Method
1 /encrypt/status 检查加密密钥是否设置成功 http://localhost:8210/encrypt/status GET
2 /encrypt 加密内容 http://localhost:8210/encrypt POST
3 /decrypt 解密内容 http://localhost:8210/decrypt POST

我们在 git 仓库重新建立一个配置文件,他主要通过非对称加密作为密文,之后通过我们的密钥来进行解密的这么一个流程:

1
2
3
4
app.version=rsa
message=Spring Cloud Config Demo
password={cipher}AQA15miINQvSMr5kJorYoX2MLH+1XgjBAGrJKPJWzAvaUhxfrd6So0skCEXjiidn/Vgf+aWCcz17pugBoRqEeDU6XLyu1tS3dAyNE/z7vbyEcp+DsJOEqHiHnr3VqzrhQTJheJOibkskvd8kJt6WP4F9Tl8qyen6mOz+4/Ce4x9iCzPoWTO2Yc+jXJFAjOSB02/kh1wnqAdyc0s7cDYLjjR+FBSMFjFJXV0Qax28hZNjjdlUb/Hy+8mPaSAQbipc9FIKB+dQaSAABYCviscHz7XkjpfmQN3qOAYiMum851OP1OOn8KHGpUu1u7D8ET+404iAHjNPTKh/As8T9A92Av73YE6b8Dj3m9wZf4hCUyEMFUpo0kffw9ThOAzVgR1RnNY=
server.port=8017

Spring Cloud Bus

Spring Cloud Bus 将会用于自动刷新配置,并且可以管理和传播分布式项目中的消息,利用消息中间件广播的机制传播可以实现将消息连接整个集群,他目前支持 Kafka 和 RabbitMQ,其中目前主要流行的就是这两个项目。

RabbitMQ


RabbitMQ 是一个开源的消息代理项目,即面向消息的中间件,采用 Erlang 语言来进行编写,因此我们除了安装 RabbitMQ 之外还需要安装 Erlang 。

对于使用 debian 系的 Linux 用户,可以通过使用 apt-get install erlang 直接进行安装 erl,之后同样的安装 RabbitMQ Server 并运行:

1
2
apt-get install rabbitmq-server
systemctl status rabbitmq-server

运行之后就开始进行配置 RabbitMQ 了,这里面主要的就是配置帐号密码以及权限和安装可视化插件:

Id Name Info
1 rabbitmq-plugins enable rabbitmq_management 安装可视化插件
2 rabbitmqctl add_user root toor 添加 root 帐号密码为 toor
3 set_user_tags root administrator 将 root 帐号设置为管理
4 set_permissions -p / root ".*" ".*" ".*" 为 root 帐号添加写入权限
5 rabbitmqctl list_permissions 查看当前的当前的权限列表

值得注意的是,当 RabbimtMQ 配置完成后,他会提供三个非常重要的端口,分别为 5672\25672\15672 在本文中主要使用 5672 端口来进行 AMQP 连接接口,而 15672 用于 HTTP 访问接口,也就是监控中心,之后你可以将这个接口添加到 Config Server & Client 配置文件中:

1
2
3
4
5
6
7
8
spring:
application:
name: SpringCloudConfigClient
rabbitmq:
username: root
password: toor
host: xxx
port: 5672

Config Server


对于 Config Server ,我们可以理解在原有的基础上,增加了 Spring Cloud Config Bus 来实现配置文件更新和广播的作用,简单来讲就是默认情况下 Config Client 是无法实现自动更新的,只可以通过 Spring Config Bus 来进行实现发送 POST 来进行刷新客户端配置文件的应用,因此我们需要添加如下依赖(在原有的非对称加密的基础上配置 Config Server):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
spring:
application:
name: cloud-server
cloud:
config:
server:
git:
uri: https://gitee.com/sif_one/spring-cloud-config
search-paths: config-repositories
username: xxx
password: xxx
encrypt:
enabled: true # 是否进行解密
rabbitmq:
username: root
password: toor
host: xxx
port: 5672
server:
port: 8210

management:
endpoints:
web:
exposure:
include: "bus-refresh"

之后打包上传到服务器之中,运行后即可通过使用 http://111.67.201.159:8210/actuator/busrefresh 接口来进行刷新客户端的配置,这是新版本中所提供的接口,当然你如果使用的是其他版本也可以通过访问 /actuator 端口来查看目前所支持的 Spring-Cloud-Bus 接口。

Config Client

Config Client 的主要作用就是获取到 Config Server 从 Git 仓库中的配置文件并运用,但是在不使用 Spring-Cloud-Config-Bus 的情况下,即使你将最新的配置文件上传了 Config Config 也不会进行更新,因此需要通过 Spring Cloud Config Bus 来进行实现,在原有的 Config CLient 基础上进行添加即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

@RefreshScope

.@RefreshScope 依赖主要的作用就是当 git 仓库发生改变或更新后,我们通过 Config server 所提供的 actuator/busrefresh POST 请求来刷新 Config Client 的配置文件,因此这个依赖主要的作用就是刷新的范围,我们在原有的 Config Server Client 基础上添加即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 获取配置中心的数据
*
* @author kunlun
* @date 2021/7/26
*/
@RestController
@RefreshScope
public class heyController {

@Value("${app.version}")
private String version;

@Value("${message}")
private String message;

@RequestMapping("/hey")
public String hey() {
String string = "message:" + message + ":" + " version:" + version;
return string;
}
}

当然你也可以在启动类中进行添加

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
application:
name: SpringCloudConfigClient
rabbitmq:
username: root
password: toor
host: xxx
port: 5672

management:
endpoints:
web:
exposure:
include: "bus-refresh"

配置文件中的 management 为开启此接口,默认的情况下是关闭的,需要通过配置文件中进行配置,之后即可访问,虽然在最新版本中不知道有没有什么作用,但是为了保险期间还是添加为好。

新版本中 Spring Cloud Config Bus 所提供的 POST 更新接口为 actuator/busrefresh

bootstrap.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: config
profile: rsa
uri: http://xxx:8210
label: master

bootstrap 配置文件主要做的就是通过 spring.cloud.config 来链接配置中心,并通过配置中心所获取的配置文件来进行运用,最后配置完成后我们可以尝试修改配置文件信息,你可以全部接口都访问一遍看看最初的效果(除了 actuator/busrefresh),之后通过 actuator/busrefresh 接口以 POST 方式进行刷新,最后访问 Spring Cloud Config Client 所提供的 /hey 接口来查看是否刷新成功。

WebHooks

WebHooks 主要的作用就是当推送到 git 仓库的时候 WebHooks 服务会将 POST 请求到对应的 uri 中,从而实现我们手动请求 POST 来刷新服务配置文件的效果。但在新版本中却请求失败,考虑其他不可抗拒的因素建议国内读者选择 Gitte,但 WebHooks 依然请求返回 400 如有读者解决可在下放进行评论。

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Spring cloud Sleuth and Zipkin

Sleuth 主要用于日志采样,而 Zipkin 用于实现服务链路追踪,他们分别是微服务中最为常用的项目之一,其中 Zipkin 可以实现数据存储(支持 ES、MySQL、cASSANDRA)以及简单部署和文档完善等优势深受开发者喜爱。

Sleuth 由 Spring cloud 团队借助 Google Dapper、Twitter Zipkin、Apache Htrace 进行设计。而 Zipkin 还有一个 OpenZipkin 项目,是 Zipkin 的完全开源版本,项目由 2021年起源与 Twitter,同样基于 Google Dapper 论文进行实现。

Dapper,一种大规模分布式系统跟踪基础设施
https://research.google/pubs/pub36356/

首先,我们需要来介绍下服务链路追踪的作用,现在很多文章一开始就跟你巴拉巴拉 Zipkin、HTrace 原理什么的,他们的基本概念就是监控微服务状态,及时发现并定位问题的点,并快速解决问题。

对于服务链路追踪,主要的就是实现监控为服务系统,来提供及时且准确的性能报告,并统计请求所耗费的时间以及具体网络延迟情况并辅助开发/运维人员分析并解决系统所存在的问题。

其实最为主要的还是通过他来快速定位故障,这也是解决问题最主要的原因 ——发现问题,通过服务链路追踪可以定位问题的故障点,并采取措施来解决问题,并提供快速检测或预警,隔离和修复问题等方式。

Google Dapper

Google Dapper 是一个在 2010 年 5 月由 Benjaim H. Sigelman, Luiz Andr’e Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul jasper, Chandan shanbhag 发布于谷歌技术报告中的一篇关于大规模分布式系统跟踪的一篇论文。

他奠定了之后的 Sleuth 以及 Zipkin 的开发与实施,都提供了前期的架构和一种概念,起初 Dapper 是一个自包含的跟踪工具,后来发展为一个监控平台。

跨度 (Span)

跨度(Span)这是一个在 Dapper 中的一个基本工作单位,发送一个 RPC 请求,就是一个新的 Span,其中还包含了其他数据,例如描述、时间戳时间、注释这些。

从上图中,客户端(前端请求)所调用的服务单元,这个过程就是一次 Span ,发起请求的客户端或前端请求的 Span id:1 ,而下一个请求的 Span id:2 ……

他们都是一个父集关系因此 Span 还有一个 parent id 也就是父集 ID,父集的 ID 为 no parent id 也被称之为 root parent,那么 Span id:2 的服务自然为parent id:1

跟踪 (Trace)

之后为了将 Span 记录的更加完整,也就是最终形成一个完整的树状结构,那么也会有一个 trace id,他的主要作用就是将 Span 标注为一个组中,在可视化界面中更加的好理解。

标注 (Annotation)

用于记录事件的存在,可以定义请求的开始和停止等信息,例如客户端发送了一个请求,Annotation 会描述的开始、服务器端获得、准本开始处理:

Id Name Info
1 cs, Client Sent 客户端发送,客户端发送了请求
2 sr,Server Received 服务端收到了请求并开始处理(根据时间戳可以从 cs ~ sr 这段时间减时间戳获取网络延迟)
3 ss,Server Sent 服务器发送,在请求处理完成时进行标注(Annotation),这个时间戳减去 sr 的时间戳,就可以得到服务器端处理所需要的时间
4 cr,Client Received 客户端收到,表示 Span 结束,客户端已经成够收到了服务器端发送的相应(因此时间戳减去 cs 就是客户端从服务器接受响应所需的全部时间)

Sleuth 日志采样

Sleuth 与 Spring Cloud 高度契合,兼容了 Zipkin、HTrace、Log-based 追踪为服务调用链路,而我们用 Sleuth 主实现的就是返回 Span\Trace id,主要使用到 spring-cloud-starter-sleuth 依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

除此之外还有 Spring cloud 全局配置,添加 sleuth 的配置项(在最新版本中无需添加),以及项目的 application.name 等:

1
2
3
4
5
6
7
8
9
spring:
application:
name: SleuthService
sleuth:
sampler:
probability: 0.3 # 设置抽样采集率为 30%,默认为 0.1 即 10%

server:
port: 8210

Application.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.logging.Logger;


/**
* 实现日志采样(msg echo log?)
*
* @author kunlun
* @date 2021/7/24
*/
@SpringBootApplication
@RestController
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

private static final Logger LOG = Logger.getLogger(DemoApplication.class.getName());

@GetMapping("/hey")
public String hey() {
LOG.info("return /hey view");
return "hello";
}
}

其中 Logger 主要的作用就是将信息输出到控制台上,也就是通过 @GetMapping 进行请求所返回的相关信息,当然你也可以配合服务集群来实现服务消费者的作用。当访问 http://localhost:8210/hey 接口后,控制台输出格式为 [应用名称,Trace id,Span id] 直观点的就是 SleuthService,9d1a88683cbacd11,9d1a88683cbacd11

如果启动后没有关于 Sleuth 相关的信息,那么可以通过 application.yml 全局配置文件中添加日志输出为 DEBUG 类型看看:

1
2
3
logging:
level:
ROOT: DEBUG

Zipkin

Zipkin 同样是基于 Google Dapper 由 Twitter 所开发的且已经被 Spring cloud 良好的集成,你可以理解为他是 Sleuth 的数据可视化版本,并且通过 Annotation 来展示服务的相关信息,但首先你需要下载 Zipkin 的控制台并运行:

1
2
curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

同时它还支持 Docker、从源代码运行等方式来启动,对于我来说通过 Jar 方式来执行已经非常便捷了,最坏的结果就是你需要先 clone 一下,然后在编译一下,之后在设置为 bin 一下,然后出错了又找一下,简单多了。

启动完后最为主要的是 http://127.0.0.1:9411/ 这也是 Zipkin 的默认端口和地址,记录下来之后在项目中进行绑定并添加依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>

实际上主要是 spring-cloud-starter-zipkin ,至于其他的只是我测试环境需要用到的,当然 spring-boot-starter-web 是可以不用加的,如果加了的话需要在配置文件中声明 main-web-application-type=reactive 来保证服务的正常启动(Gateway 已经存在了 web)

Application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
spring:
application:
name: Zipkin
cloud:
gateway:
routes:
- id: hey
uri: lb://service-provider/hey
predicates:
- Path=/hey
discovery:
locator:
# 允许服务发现
enabled: true
consul:
host: localhost
port: 8500
discovery:
service-name: service-provider
zipkin:
base-url: http://localhost:9411
sender:
type: web
main:
web-application-type: reactive
server:
port: 8210

之后请求相应的接口并重新在 Zipkin 控制台上点击 RUN QUERY 刷新下请求列表即可获取当前服务的响应以及依赖关系等更加详细的视图,读者可根据自身需要来进行查阅,但唯一不好的就是 Zipkin 不是异步刷新,需要手动点……,以及在查看详细请求数据的时候界面没有返回按钮(也许设计起初是为了靠浏览器自带的 <- -> 来实现窗口的返回)。

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Spring cloud 限流及限流算法

限流的主要目的是防止过渡的消耗资源,从而来保障服务对所有用户的可用性,常见的限流措施如诸以限制并发数为目的的数据库连接池/线程池,以及瞬时并发数的限制,和限制每秒的平均速率都是最为常见的。而这些基本上都会在网关层上进行实现,如使用 nginx、zuul、gateway、openresty、kong 等方式进行限流。

以维基百科的话来说就是在计算机网络中,控制网络接口请求速率

如果没有进行限流的话,则有几率会被恶意提交,导致后续服务无法得到提供,从而达到拒绝服务攻击,而限流在高并发中,是一个必不可少的一个因素之一,通过限流发挥的作用从而保护用户的可用性,甚至防止一些恶意请求等问题的发生。

限流算法

计数器算法(固定窗口)

计数器算法是一个简单以及暴力的,他从第一个请求开始计数,在假设我们设置一个 QPS(每秒查询数为 100),一秒内请求超过了设定值,那么接下来的请求都会被拒绝,等过一秒后才可继续查询,这时会将计数恢复成0。

看似这个算法很好且可以解决一些简单的问题,但这个算法也存在的一些问题,如我在 100ms 内通过了 100 个请求,则后续的 900ms 内的请求都会被拒绝,因此这种现象也被称之为 “突刺现象”。虽然我们可以通过将单位时间设置的更短,但依旧无法解决核心的问题,这时就诞生了漏桶算法和令牌桶算法。

计数器算法(滑动窗口)

滑动窗口又是计数器算法的一个改进,主要解决了固定窗口上的 临界值 的问题,将一个计时器分为了诺干个小的计时器,这些计时器都是独立的,当请求时间大于第一个计时器最大时间时,那么将会向前平移一个计时器(同时将前一个计时器丢弃,然后将当前的计时器设置为第一个计时器)。

如上图中,窗口大小为 1s,那么每过 0.2ms就会删除前一个格子,从而向右移一个格子,滑动窗口最为主要的就是他的滑动,使得可以控制的更加细密,以此来解决临界值的问题,但滑动窗口和固定窗口都无法解决突刺现象这一问题。

漏桶算法

漏桶算法(Leaky Bucket)内主要有两个变量,分别为 桶的大小以及漏洞(孔的大小)的大小,可以将请求比喻水,当水倒入漏桶中,需要有一定的处理速度(孔的大小决定处理的速度),而桶的大小决定了可以容纳请求的大小,如果请求太大,则会导致漏桶装不下请求从而拒绝后续的请求。

令牌桶算法

令牌桶算法(Token Bucket)这种算法和我们在医院排队叫号比较相似,当请求到达的时候,会去令牌桶(由令牌工厂生产令牌)中取得一个令牌,之后等待响应。

这也达到了一定限流的目的,首先我们可以决定令牌工厂放入令牌的速率,也可以定时或者根据一些特定的规则来达到目的,从而达到增加/减少令牌的数量达到限流的效果。

限流的实现

Spring cloud Gateway 内置了限流工厂 RequestRateLimiterGatewayFilterFactory 底层使用 redis lua 脚本进行实现,因此需要添加 Consul、Gateway、Redis 的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis-reactive -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

之后编写我们的限流配置类,主要通过名称、地址、以及 API 进行限流三种,也可以根据不同的状况自定义配置(还有一个跟均 CPU 进行限流的?)

LimitConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.demo.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
* 限流配置文件,根据名称、地址、API
*
* @author kunlun
* @date 2021/7/23
*/
@Component
public class LimitConfig {

@Bean
KeyResolver userKeyResolver() {
return exchange ->
Mono.just(exchange.getRequest().getQueryParams().
getFirst("user"));
}
@Primary
@Bean
KeyResolver ipKeyResolver() {
return exchange ->
Mono.just(exchange.getRequest().getRemoteAddress().
getHostName());
}

@Bean
KeyResolver apiKeyResolver() {
return exchange ->
Mono.just(exchange.getRequest().getPath().
value());
}
}
application.yml

最后通过配置文件 application.yml 来链接 consul、redis 以及配置路由规则和全局过滤等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
spring:
application:
name: GatewayLimit
redis:
host: localhost
port: 6379
database: 1
password: toor
cloud:
gateway:
routes:
- id: hey
uri: lb://service-provider/hey
predicates:
- Path=/hey
- Method=GET
filters:
- name: RequestRateLimiter # 用于确定当前请求是否继续(如果不允许则会返回 429
args:
key-resolver: '#{@ipKeyResolver}'
redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充的平均速率
redis-rate-limiter.burstCapacity: 3 # 令牌桶总流量
discovery:
locator:
# 允许服务发现
enabled: true
consul:
host: localhost
port: 8500
discovery:
service-name: service-provider

server:
port: 8210

保证 redis、consul 等已经启动且可提供服务

端点

端点在 Spring cloud Gateway 中主要提供的作用就是方便开发或者运维人员进行调试,以此来获取过滤器列表、路由列表、路由信息、刷新路由信息以及添加\删除路由等操作,这依赖于 Spring boot Actuator ,需要注意的是这些端点都会在 /actuator/gateway 路径下,分别提供对应的服务:

Id Name Method Info Uri
1 globalfilters GET 展示所有全局过滤器 /actuator/gateway/globalfilters
2 routefilters GET 展示所有过滤器工厂 /actuator/gateway/routefilters
3 refresh POST 清空路由缓存 /actuator/gateway/refresh
4 routes GET 获取路由列表 /actuator/gateway/routes
5 routes{name} GET 获取指定的路由信息 /actuator/gateway/routes/{name}
6 routes{name} POST 添加一个路由 /actuator/gateway/routes/{name}
7 routes{name} DELETE 移出一个路由 /actuator/gateway/routes/{name}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis-reactive -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.5.2</version>
</dependency>

.

1
2
3
4
5
6
……
management:
endpoints:
web:
exposure:
include: '*'

开启后,可以访问 http://localhost:8210/actuator/gateway/routes/hey 来进行测试是否可以该路由的信息,如果没有返回则可能是 Actuator 配置上有一点点的问题

management 中,默认只开启了两个端点,分别为 health、info 两个 因此我们可以通过上述配置开启全部的断点访问,更详细的可以查阅 Actuator API: https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

有序集合(Zset)

有序集合(zset)和集合同样都是一个字符串类型的集合元素,他们都不允许同样重复的成员。不同的是他拥有一个双精度浮点数来达到从达到小排序的效果,与集合一样可以存储越 40亿个成员。

作为 Redis 几个最为主要的数据类型,他同样拥有针对性的命令,分别为 ZADD 即添加有序集合,他可以添加多个或单个(取决与你的习惯),当然也有 ZRANGE 来进行输出。

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> ZADD key_zset 1 sql 2 mysql 3 mongoDB 4 redis
(integer) 1
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "sql"
2) "1"
3) "mysql"
4) "2"
5) "mongoDB"
6) "3"
7) "redis"
8) "4"

ZRANGE(从小到大)


ZRANGE 命令会按照数值从小到大来进行排序(具有相同数值的成员将会被按照字典序列来进行排序)

字典序列也就是根据 A、B、C、D 这个序列来进行从小到大来进行划分,也就是根据首字母开头进行排序。

在通过 ZRANGE 命令进行查询的时候,如果不加上 WITHSCORES 返回的是一个元素列表,加上的话返回的则是一个数组列表。

1
2
3
4
5
127.0.0.1:6379> ZRANGE key_zset 0 -1
1) "sql"
2) "mysql"
3) "mongoDB"
4) "redis"

ZRANGEBYLEX

当集合中的元素都以相同的方式进行插入时,那么将会强制按照字典顺序来进行排列(在 Redis 中默认为英文字典 ABC以此类推进行排序)。

在这个命令中他支持有效的 start 以及 stop 修饰符,分别为 [ 以及 - \ + 等。其中 -/+ 分别表示最小和最大字符串,也就是开始和结尾,而 [/( 的区别是输出时是否包含元素 [ 是包含 ( 即不包含。

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> ZRANGEBYLEX key_zset - +
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379> ZRANGEBYLEX key_zset - (b
1) "a"
127.0.0.1:6379> ZRANGEBYLEX key_zset - [b
1) "a"
2) "b"

ZRANGEBYSCORE

ZRANGEBYSCORE 与 ZRANGEBYLEX 相似,但以我个人感觉更喜欢后者,前者的 -inf+inf 与后者的 -/+ 功能基本一样,区别是多了个 ”inf“,而之后的 [( 功能也很一样,但将需要通过元素来作为参数改为了数值(在ZRANGEBYSCORE 中 [ 并不支持)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> ZRANGEBYSCORE key_zset 1 2
1) "one"
2) "two"
127.0.0.1:6379> ZRANGEBYSCORE key_zset -inf 2
1) "day"
2) "one"
3) "two"
127.0.0.1:6379> ZRANGEBYSCORE key_zset -inf (2
1) "day"
2) "one"
127.0.0.1:6379> ZRANGEBYSCORE key_zset -inf +inf
1) "day"
2) "one"
3) "two"
4) "three"

ZSCORE

ZSCORE 命令是上述几个查询命令中最为朴素的命令之一,他主要根据成员名称来返回在其集合中的索引号

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> ZRANGE zset_one 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
127.0.0.1:6379> ZSCORE zset_one "day"
"0"

ZCOUNT

ZCOUNT 同样支持 ( 以及 -inf\+inf这类的表达式,他主要的作用就是计算出集合中的成员数。

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> ZRANGE zset_two 0 -1
1) "day"
2) "one"
3) "two"
4) "three"
5) "four"
127.0.0.1:6379> ZCOUNT zset_two -inf +inf
(integer) 5
127.0.0.1:6379> ZCOUNT zset_two -inf 3
(integer) 4
127.0.0.1:6379> ZCOUNT zset_two -inf (3
(integer) 3

ZREVRANK(从大到小)

ZREVRANK 同样是从大到小根据 0 为起始点进行排序,与 ZREVEANGE 类似,但该命令主要通过成员来返回在其集合中的索引。

1
2
3
4
5
6
7
127.0.0.1:6379> ZRANGE zset_one 0 -1
1) "day"
2) "one"
3) "two"
4) "three"
127.0.0.1:6379> ZREVRANK zset_one "day"
(integer) 3

ZREVRANGEBYSCORE

ZREVRANGEBYSCORE 可以理解为是 ZREVRANK 的延续之作,他除了索引之外还支持 ZRANGEBYLEX 其中的表达式,除此之外还整合了 ZRANGEBYSCORE 来打通信息屏障,通过差异化和颗粒度达到引爆点来聚焦用户感知赛道。

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> ZREVRANGEBYSCORE zset_one 4 1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> ZREVRANGEBYSCORE zset_one 4 (1
1) "three"
2) "two"
127.0.0.1:6379> ZREVRANGEBYSCORE zset_one +inf -inf
1) "three"
2) "two"
3) "one"
4) "day"

ZREVEANGE

ZREVRANGE 是一个更加纯粹的返回字典序列的一个命令,他比 ZRANGE 这群命令显得更加的纯粹和干练,同样是返回集合的作用,该命令只需要指定索引即可,不支持任何华丽胡少的表达式(但支持 redis 自带的)

1
2
3
4
5
127.0.0.1:6379> ZREVRANGE zset_one 0 -1
1) "three"
2) "two"
3) "one"
4) "day"

ZINCRBY

ZINCRBY 简单来讲就是对集合元素中的数组进行增加,假设集合中元素的数组为 0,那么即可通过 ZINCRBY 来进行增加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> ZINCRBY key_zset 10 "three"
"13"

127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"
7) "three"
8) "13"
127.0.0.1:6379> ZINCRBY key_zset -10 "three"
"3"

并集/合集

ZINTERSTORE(合集)

将两个集合之间的交集存储到另一个集合中,需要指定两个集合中相同数的数值(也就是最终相同数所存储到另一个集合中的数量),默认情况下,结果集中的元素数值是这些集合中元素数值之和。

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> ZINTERSTORE zset_key 2 zset_one zset_two
(integer) 3
127.0.0.1:6379> ZRANGE zset_key 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "2"
5) "two"
6) "4"

ZUNIONSTORE (并集)

ZUNIONSTORE 与 ZINTERSTORE 的区别就是一个是合集另一个是并集的一字之差,说简单一点就是将两个集合进行合并,相同的元素剔除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
127.0.0.1:6379> ZRANGE zset_two 0 -1
1) "day"
2) "one"
3) "two"
4) "three"
5) "four"
127.0.0.1:6379> ZRANGE zset_one 0 -1
1) "day"
2) "one"
3) "two"
4) "three"
127.0.0.1:6379> ZUNIONSTORE zset_key 2 zset_one zset_two
(integer) 5
127.0.0.1:6379> ZRANGE zset_key 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "2"
5) "four"
6) "4"
7) "two"
8) "4"
9) "three"
10) "6"

但这样会造成集合中索引的相同,为了避免这类事情的发生我们可以自定义权重或和来解决 ZUNIONSTORE zset_key 2 zset_one zest_two WEIGHTS 2 3 [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX],这同样在 ZINTERSTORE 命令中适用。

ZRANK

ZRANK 简单来说就单纯的是一个可以根据其集合成员来返回其索引的作用,如果其成员不存在则会返回 nil

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> ZRANK zset_one "day"
(integer) 0
127.0.0.1:6379> ZRANK zset_one "one"
(integer) 1
127.0.0.1:6379> ZRANGE zset_one 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"

ZREM

ZREM 没有像后续几条命令一样那么否有诗书气自华,他主要根据成员来针对单个集合进行删除,在 Redis => 2.4 版本中支持删除多个成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> ZADD key_zset 0 day 1 one 2 two 3 three 3 four
(integer) 5
127.0.0.1:6379> ZRANGE key_zset 0 -1
1) "day"
2) "one"
3) "two"
4) "four"
5) "three"
127.0.0.1:6379> ZREM key_zset "four"
(integer) 1
127.0.0.1:6379> ZRANGE key_zset 0 -1
1) "day"
2) "one"
3) "two"
4) "three"
127.0.0.1:6379>

ZREMRANGEBYLEX

该命令与 ZREM 相比起来就非常的丰富,他支持了一些较为简单的 Redis 表达式([ 包括自己),适合多个成员一起删除,当然你也可以选择删除单个(弊端就是你删除不了第一个和倒数第二个成员)。

但是 ZREMRANGEBYLEX 的弊端就是需要该集合索引都是一样的才可以进行删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "day"
2) "0"
3) "four"
4) "0"
5) "one"
6) "0"
7) "three"
8) "0"
9) "two"
10) "0"
127.0.0.1:6379> ZREMRANGEBYLEX key_zset [day [one
(integer) 3
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "three"
2) "0"
3) "two"
4) "0"

ZREMRANGEBYRANK

该命令同样非产简约优雅,通过 start\stop 的操作可以快速删除多个集合中的成员,但劣势也一样展现出来,他并不能删除单个成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
9) "four"
10) "5"
127.0.0.1:6379> ZREMRANGEBYRANK key_zset 3 5
(integer) 2
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"

ZREMRANGEBYSCORE

ZREMRANGEBYSCORE Reids 的有序列表删除命令就发展的非常特别且简单优雅,他同样适合于多个成员的删除操作,与前者不同的是到他这开始支持了 -inf (不包含该成员) 这类的表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "day"
2) "0"
3) "one"
4) "1"
5) "two"
6) "2"
127.0.0.1:6379> ZREMRANGEBYSCORE key_zset -inf (1
(integer) 1
127.0.0.1:6379> ZRANGE key_zset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Redis 集合(Set)

集合(set)是一个字符串类型的无序集合,集合成员是唯一的,因此这意味着在集合中不可以有重复的成员数据,每个集合中最大可以存储40多亿个成员数据。通常可以通过 SADD 创建一个集合并向其添加成员(一个或多个),之后 SMEMBERS 来返回集合中的所有成员。

1
2
3
4
5
6
7
127.0.0.1:6379> SADD set_key sql mysql redis mongoDB 
(integer) 4
127.0.0.1:6379> SMEMBERS set_key
1) "mongoDB"
2) "redis"
3) "mysql"
4) "sql"
Id Name Info Command
1 SADD 创建集合并向其中添加一个或多个成员 SADD set_key sql mysql redis mongoDB
2 SINTERSTORE 将两个集合中相同的成员保存在另一个集合中 SINTERSTORE set_key set_one set_two
3 SDIFFSTORE 将两个集合中不相同的成员数据保存到另一个集合中 SDIFFSTORE set_key set_one set_two (将 set_one 和 set_two 两个集合之间不相同的成员保存到 set_key 集合中)
4 SUNIONSTORE 将两个或多个集合之间的成员进行合并到另一个集合中 SUNIONSTORE key_set set_one set_two
5 SMOVE 将一个集合中的成员移动到另一个集合中 SMOVE set_one set_two "sql" (将 set_one 集合中的成员“sql” 移动到 set_two 中)
6 SDIFF 返回指定集合中的不同成员 SDIFF set_two set_one
7 SINTER 返回指定集合中相同的成员 SINTER set_one set_two
8 SUNION 返回将指定集合合并后的并集 SUNION set_one set_two
9 SMEMBERS 返回指定集合中所有的成员 SMEMBERS set_key
10 SISMEMBER 判断集合中成员是否存在(存在返回1,不存在返回0) SISMEMBER set_one "mysql"
11 SSCAN 遍历指定集合中所存在的键元素 SSCAN set_two 0 MATCH s* (匹配该集合中所有以 “s” 开头的成员 )
12 SRANDMEMBER 随机返回指定集合中的成员数(如果数大于成员数那么将返回所有成员) SRANDMEMBER set_two 10
13 SPOP 根据指定数值随机删除集合成员,并返回所删除的成员数据 SPOP set_two 3
14 SREM 删除一个或多个集合中的成员 SREM set_two "mysql"

SINTERSTORE


SINTERSTORE 命令在 Redis 中的主要作用就是将两个集合中相同的成员数据添加到另一个集合中。

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> SMEMBERS set_one
1) "redis"
2) "mysql"
3) "sql"
127.0.0.1:6379> SMEMBERS set_two
1) "mongoDB"
2) "mysql"
3) "redislab.com"
127.0.0.1:6379> SINTERSTORE set_key set_one set_two
(integer) 1
127.0.0.1:6379> SMEMBERS set_key
1) "mysql"

SDIFF


返回两个集合中不相同的成员数据,相同的数据将不会被输出,按照官方的意思说这个命令主要返回地一个集合与其他集合之间的差异(说人话就是将其他集合不相同的数据进行输出)

1
2
3
4
5
6
7
127.0.0.1:6379> SDIFF set_two set_one
1) "mongoDB"
2) "redislab.com"

127.0.0.1:6379> SDIFF set_one set_two
1) "redis"
2) "sql"

在集合中有两个非常重要的名词,分别是交集和差集,交集就是相同的意思,而差集就不相同的意思。

SDIFFSTORE

SDIFFSTORE 与 SDIFF 类似,但他主要的作用就是将两个集合之间的不相同数据(也就是差值)保存到另一个集合之中

1
2
3
4
5
127.0.0.1:6379> SDIFFSTORE set_key set_one set_two
(integer) 2
127.0.0.1:6379> SMEMBERS set_key
1) "redis"
2) "sql"

SINTER


SINTER 与 SDIFF 不同之处在于,SINTER 用于返回所有集合中的交集,也就是相同的成员集合。

1
2
127.0.0.1:6379> SINTER set_one set_two
1) "mysql"

SINTERSTORE

Redis 是一个考虑很全面的 key-value 数据库,因此他的命令也非常的具有针对性,该命令与 SDIFF 中的 SDIFFSTORE 非常相似,但该命令是将两个集合中的交集存储到另一个集合中:

1
2
3
4
127.0.0.1:6379> SINTERSTORE set_key set_one set_two
(integer) 1
127.0.0.1:6379> SMEMBERS set_key
1) "mysql"

SUNION


SUNION 简单来说就是将两个集合并集,也就是将两个集合合并,将相同的数据所剔除,与 SDIFF 和 SINTER 一样,分为输出和存储两个非常具有针对性的命令。

1
2
3
4
5
6
127.0.0.1:6379> SUNION set_one set_two
1) "sql"
2) "redis"
3) "mysql"
4) "mongoDB"
5) "redislab.com"

所谓“并集” 也就是将两个或多个集合成员进行合并,如果不同集合之间的成员数据相同将会被剔除。

SUNIONSTORE

SUNIONSTORE 同样集成了 SUNION 命令逻辑,只不过是将输出改为了存储,将两个或多个集合之间的成员进行并集

1
2
3
4
5
6
7
8
127.0.0.1:6379> SUNIONSTORE key_set set_one set_two
(integer) 5
127.0.0.1:6379> SMEMBERS key_set
1) "sql"
2) "redis"
3) "mysql"
4) "mongoDB"
5) "redislab.com"

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Redis 列表(List)

列表在 Redis 中表示可以将数据按顺序排序插入的字符串列表,通常可以包含超过40亿个元素,这启动主要通过 LPUSH 来插入一个或多个到列表头部,以及通过 LRANGE 来获取列表范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> LPUSH list_key redis
(integer) 1
127.0.0.1:6379> LPUSH list_key mysql
(integer) 2
127.0.0.1:6379> LPUSH list_key sql
(integer) 3
127.0.0.1:6379> LPUSH list_key mongoDB
(integer) 4
127.0.0.1:6379> LPUSH list_key one two
(integer) 6
127.0.0.1:6379> LRANGE list_key 0 10
1) "two"
2) "one"
3) "mongoDB"
4) "sql"
5) "mysql"
6) "redis"
Id Name Info Command
1 LPUSH 将一个或多个值插入到列表头部 (如果 key 不存在则会创建一个) LPUSH list_key mongoDB
2 RPUSH 将一个或多个值插入到表尾部(如果 key 不存在则会创建一个) RPUSH list_key "nice!"
3 LPUSHX key 不存在时不进行任何操作并返回 0,存在的话则将值插入到列表头部 LPUSHX list_key "hello"
4 RPUSHX key 不存在时不进行任何操作并返回 0,存在的话则将值插入到列表尾部 RPUSHX list_keys "world"
5 LINSERT 在指定元素前插入列表数据 LINSERT list_key BEFORE "redis" "redislab.com" (在 redis 前插入 redislab.com)
6 LSET 通过索引替换列表元素(以0开始) LSET list_key 0 "start"(将 索引为0的元素替换为 “start”)
7 LRANGE 在一定范围内获取列表内元素 LRANGE list_key 0 100
8 LINDEX 返回指定索引对应的表内元素(超出范围返回 nill) LINDEX list_key 0
9 BRPOPLPUSH 将指定列表内最后一个元素插入到另外一个列表的头部(如果列表没有元素会阻塞到超时为止,或者发现可以移出的元素为止,单位是秒,需要注意的是如果超时参数是 0 ,那么将会表示阻塞的时间可以无限期延长) BRPOPLPUSH list_key key_name 500
10 LLEN 获取列表的元素数量 LLEN list_key
11 LTRIM 用于修建已经存在列表内的元素(要么从前删除要么从后删除,不可跨越索引) LTRIM list_key -1 -1 (从倒数地一个开始,然后直到倒数第一个,也就是说之保留倒数地一个)
12 BLPOP 移出第一个列表元素,并返回所移出的列表名称和元素 BLPOP list_key 0
13 BRPOP 移出列表最后一个元素,同样返回岁移出的列表名和元素 LRANGE list_key 0 -1
14 LPOP 移出列表中地一个元素(与 BLPOP 的区别是不用设置阻塞时间) LPOP list_key 1
15 LREM 指定一个或多个列表中相同元素进行删除 LREM list_key 1 "sql"

LTRIM


在 Redis 中 LTRIM 主要用于修剪(删除)已经存在的列表内的元素,其中分为 start 以及 stop 以 0 为开始,-1 可以表示列表里面的最后一个元素,-2 表倒数第二个 ……(这适用绝大多数依赖索引所完成的命令,如 RANGE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> LRANGE list_key 0 1000
1) "start"
2) "two"
3) "one"
4) "mongoDB"
5) "sql"
6) "mysql"
7) "redislab.com"
8) "redis"
9) "world"
127.0.0.1:6379> clear

127.0.0.1:6379> LTRIM list_key -1 -1
OK
127.0.0.1:6379> LRANGE list_key 0 100
1) "world"

要么从前删除要么从后删除,不可跨越索引

我们所构造的 -1~-1 翻译到人可以理解的那就是,从倒数地一个开始,然后直到倒数第一个,也就是说之保留倒数地一个,当然你也可以通过 LTRIM list_key -6 -1 从倒数第六到最后一个来进行保留,删除其余外的元素:

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> LTRIM list_key -6 -1
OK
127.0.0.1:6379> LRANGE list_key 0 -1
1) "mongoDB"
2) "sql"
3) "mysql"
4) "redislab.com"
5) "redis"
6) "world"

BLPOP

主要的作用就是移出指定列表中的第一个元素,并返回所移出的元素名称,如果列表没有元素或没有该列表,则会等待超时时间过后即可关闭阻塞,或者得到发现可以移出的元素为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> LRANGE list_key 0 -1
1) "world"
2) "redis"
3) "redislab.com"
4) "mysql"
5) "sql"
6) "mongoDB"
7) "one"
8) "two"
9) "start"
127.0.0.1:6379> BLPOP list_key 0
1) "list_key"
2) "world"
127.0.0.1:6379> LRANGE list_key 0 -1
1) "redis"
2) "redislab.com"
3) "mysql"
4) "sql"
5) "mongoDB"
6) "one"
7) "two"
8) "start"

LTRIM


LTRIM 与列表中所有的移出命令不同的是,他可以指定一个或多个在列表中相同的元素进行删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> LPUSH list_key "sql" sql sql
(integer) 6
127.0.0.1:6379> LRANGE list_key 0 -1
1) "sql"
2) "sql"
3) "sql"
4) "redislab.com"
5) "mysql"
6) "mongoDB"
127.0.0.1:6379> LREM list_key 1 "sql"
(integer) 1
127.0.0.1:6379> LRANGE list_key 0 -1
1) "sql"
2) "sql"
3) "redislab.com"
4) "mysql"
5) "mongoDB"

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布

Redis 哈希(Hash)

Redis 中的数据类型主要分为 字符串、哈希、列表、集合、有序集合、HyperLogLog、地理信息、Stream 等类型,字符串类型在上一章我们就已经介绍过,他通过 SET 就可以直接进行创建,而其他的数据类型则是需要通过其他的命令以及其下属配合的命令来完成操作。

哈希是一个 string 类型的 字段(field) 以及值(value)映射表,可适用与存储对象,单个哈希键值可存储多大40亿字段值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> HMSET hash_value name "kun" info "hello,world" url "http://jiangxue.org.cn" and "one" or "two" age "17" redis "redislabs.com"
OK
127.0.0.1:6379> HGETALL hash_value
1) "name"
2) "kun"
3) "info"
4) "hello,world"
5) "url"
6) "http://jiangxue.org.cn"
7) "and"
8) "one"
9) "or"
10) "two"
11) "age"
12) "17"
13) "redis"
14) "redislabs.com"

需要注意的是 Hashes 类型的字段依然是通过键值来进行存储的,如 name=kun、info=hello,world

可以通过使用 HMSET 来创建 hash 类型的键值对,也可以使用 HGETALL 来获取所有的字段值,当然也可以指定键名的方式来进行获取 HGETALL hash_value "name"

Id Name Info Command
1 HMSET 创建 hash 类型的键值段 HMSET hash_value name "kun" info "hello,world" url "http://jiangxue.org.cn"
2 HMGET 获取指定字段的值 HMGET hash_value name
2 HGET 获取存储在哈希表中指定的字段值 HGET hash_value "name"
3 HSET 自 redis 4.0 起可以一次性设置多个字段对,HMSET 区别是可以覆盖字段值 HSET hash_value age "10"
4 HSETNX 为哈希表中不存在的字段赋值(如存在返回 0) HSETNX hash_value about "jiangxue"
5 HGETALL 获取全部或单个的 hash 类型键值段 HGETALL hash_value or HGET hash_value "name"
6 HEXISTS 查看哈希表中的 key 是否存在(存在返回 1,否则返回0) HEXISTS hash_value "name"
7 HKEYS 获取当前所有哈希表中的字段 HKEYS hash_value
8 HVALS 获取当前所有哈希表中的字段值 HVALS hash_value
9 HLEN 获取哈希表中字段的数量(从1开始) HLEN hash_value
10 HDEL 删除一个多多个哈希表中的键值对 HDEL hash_value "and" "or"
11 HINCRBY 为指定的哈希表中的 key 增加或减少数值(整数) HINCRBY hash_value age 1 (原本 17将会被增加1,得出 18)
12 HINCRBYFLOAT HINCRBY 的区别是支持非整数计算以及科学计数法 HINCRBYFLOAT hash_value age -1.5 (18-1.5 得出 16.5)
13 HSCAN 模糊搜索哈希表中的字段,并返回键值段 HSCAN hash_value 0 match "na*" (返回 name 字段即值)
14 HSTRLEN 返回字段值的长度 HSTRLEN hash_value "name"

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布